package org.kjs.common.clone.implement;

import org.kjs.common.clone.AccessibleFieldStructure;
import org.kjs.common.clone.CloneableConstruct;
import org.kjs.common.clone.CloneableStructure;

import java.lang.reflect.Method;
import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: KJS
 * Date: 11-8-15
 * Time: 下午8:04
 * To change this template use File | Settings | File Templates.
 */
public class CloneableStructurer<V> implements CloneableStructure<V>{

    private Class<V> cloneableItemClass;

    CloneableStructurer (Class<V> cloneableItemClass) {
        this.cloneableItemClass = cloneableItemClass;
    }

    protected AccessibleFieldStructure<V>[] parseAccessibleFields (Map<String, AccessibleField<V>> fields) {
        AccessibleFieldStructure<V>[] accessibleFieldsArray = null;

        if (null != fields) {
            List<AccessibleField<V>> accessibleFields = new ArrayList<AccessibleField<V>>();
            for (AccessibleField<V> field : fields.values()) {
                if (field.isAvailableAccessibleField()) {
                    accessibleFields.add(field);
                } else {
                    Method anotherMethod = null;
                    Method getMethod = field.getAccessibleFieldGetMethod();
                    if (null != getMethod) {
                        try {
                            anotherMethod = getMethod.getDeclaringClass().getMethod(
                                    AccessibleFieldStructure.ACCESSIBLE_FIELD_METHOD_SET+field.getName(),
                                    new Class [] {getMethod.getReturnType()});
                        } catch (NoSuchMethodException e) {
                            anotherMethod = null;
                        }
                    }
                    Method setMethod = field.getAccessibleFieldSetMethod();
                    if (null != setMethod) {
                        Class<?> parameterType = setMethod.getParameterTypes()[0];
                        String parameterTypeName = parameterType.getName();
                        if ("boolean".equals(parameterTypeName) || "java.lang.Integer".equals(parameterTypeName)) {
                            try {
                                anotherMethod = setMethod.getDeclaringClass().getMethod(
                                        AccessibleFieldStructure.ACCESSIBLE_FIELD_METHOD_IS+field.getName(),new Class [] {});
                            } catch (NoSuchMethodException e) {
                                try {
                                    anotherMethod = setMethod.getDeclaringClass().getMethod(
                                            AccessibleFieldStructure.ACCESSIBLE_FIELD_METHOD_GET+field.getName(),new Class [] {});
                                } catch (NoSuchMethodException e1) {
                                    anotherMethod = null;
                                }
                            }
                        } else {
                            try {
                                anotherMethod = setMethod.getDeclaringClass().getMethod(
                                                AccessibleFieldStructure.ACCESSIBLE_FIELD_METHOD_GET+field.getName(),new Class [] {});
                            } catch (NoSuchMethodException e) {
                                anotherMethod = null;
                            }
                        }
                        if (null != anotherMethod) {
                            if (anotherMethod.getName().startsWith(AccessibleFieldStructure.ACCESSIBLE_FIELD_METHOD_SET)) {
                                field.setAccessibleFieldSetMethod(anotherMethod);
                            } else {
                                field.setAccessibleFieldGetMethod(anotherMethod);
                            }
                            if (field.isAvailableAccessibleField()) {
                                accessibleFields.add(field);
                            }
                        }
                    }
                }
            }
            accessibleFieldsArray = new AccessibleFieldStructure[accessibleFields.size()];
            accessibleFieldsArray = accessibleFields.toArray(accessibleFieldsArray);
        }
        return accessibleFieldsArray;
    }

    public AccessibleFieldStructure<V>[] getAllAccessibleFields() {
        AccessibleFieldStructure<V>[] accessibleFieldStructures = null;

        if (null != cloneableItemClass) {
            Map<String, AccessibleField<V>> cache = null;

            if (null != cloneableItemClass) {
                cache = new HashMap<String, AccessibleField<V>>();
            }

            for (Method method : cloneableItemClass.getMethods()) {
                AccessibleField<V> field = new AccessibleField<V>(cloneableItemClass);
                field.setAccessibleFieldGetMethod(method);
                field.setAccessibleFieldSetMethod(method);

                String fieldName = field.getName();
                if (null != fieldName) {
                    if (cache.containsKey(fieldName)) {
                        AccessibleField<V> cacheField = cache.remove(fieldName);
                        field.setAccessibleFieldGetMethod(cacheField.getAccessibleFieldGetMethod());
                        field.setAccessibleFieldSetMethod(cacheField.getAccessibleFieldSetMethod());
                    }
                    synchronized (cache) {
                        cache.put(fieldName, field);
                    }
                }
            }

            accessibleFieldStructures = parseAccessibleFields(cache);
        }

        return accessibleFieldStructures;
    }

    public CloneableConstruct<V> getCloneableConstruct() {
        CloneableConstructor<V> cloneableConstruct = new CloneableConstructor<V>();
        cloneableConstruct.setCloneableItemClass(cloneableItemClass);
        cloneableConstruct.getConstructor();
        return cloneableConstruct;
    }

}
